package types

// Reference: https://www.ietf.org/rfc/rfc4120.txt
// Section: 5.2.5

import (
	
	
	

	
	
)

// HostAddresses implements RFC 4120 type: https://tools.ietf.org/html/rfc4120#section-5.2.5
type HostAddresses []HostAddress

// HostAddress implements RFC 4120 type: https://tools.ietf.org/html/rfc4120#section-5.2.5
type HostAddress struct {
	AddrType int32  `asn1:"explicit,tag:0"`
	Address  []byte `asn1:"explicit,tag:1"`
}

// GetHostAddress returns a HostAddress struct from a string in the format <hostname>:<port>
func ( string) (HostAddress, error) {
	var  HostAddress
	, ,  := net.SplitHostPort()
	if  != nil {
		return , fmt.Errorf("invalid format of client address: %v", )
	}
	 := net.ParseIP()
	var  int32
	if .To4() != nil {
		 = addrtype.IPv4
		 = .To4()
	} else if .To16() != nil {
		 = addrtype.IPv6
		 = .To16()
	} else {
		return , fmt.Errorf("could not determine client's address types: %v", )
	}
	 = HostAddress{
		AddrType: ,
		Address:  ,
	}
	return , nil
}

// GetAddress returns a string representation of the HostAddress.
func ( *HostAddress) () (string, error) {
	var  []byte
	,  := asn1.Unmarshal(.Address, &)
	return string(), 
}

// LocalHostAddresses returns a HostAddresses struct for the local machines interface IP addresses.
func () ( HostAddresses,  error) {
	,  := net.Interfaces()
	if  != nil {
		return
	}
	for ,  := range  {
		if .Flags&net.FlagLoopback != 0 || .Flags&net.FlagUp == 0 {
			// Interface is either loopback of not up
			continue
		}
		,  := .Addrs()
		if  != nil {
			continue
		}
		for ,  := range  {
			var  net.IP
			switch v := .(type) {
			case *net.IPNet:
				 = .IP
			case *net.IPAddr:
				 = .IP
			}
			var  HostAddress
			if .To16() == nil {
				//neither IPv4 or IPv6
				continue
			}
			if .To4() != nil {
				//Is IPv4
				.AddrType = addrtype.IPv4
				.Address = .To4()
			} else {
				.AddrType = addrtype.IPv6
				.Address = .To16()
			}
			 = append(, )
		}
	}
	return , nil
}

// HostAddressesFromNetIPs returns a HostAddresses type from a slice of net.IP
func ( []net.IP) ( HostAddresses) {
	for ,  := range  {
		 = append(, HostAddressFromNetIP())
	}
	return 
}

// HostAddressFromNetIP returns a HostAddress type from a net.IP
func ( net.IP) HostAddress {
	if .To4() != nil {
		//Is IPv4
		return HostAddress{
			AddrType: addrtype.IPv4,
			Address:  .To4(),
		}
	}
	return HostAddress{
		AddrType: addrtype.IPv6,
		Address:  .To16(),
	}
}

// HostAddressesEqual tests if two HostAddress slices are equal.
func (,  []HostAddress) bool {
	if len() != len() {
		return false
	}
	for ,  := range  {
		var  bool
		for ,  := range  {
			if .Equal() {
				 = true
				break
			}
		}
		if ! {
			return false
		}
	}
	return true
}

// HostAddressesContains tests if a HostAddress is contained in a HostAddress slice.
func ( []HostAddress,  HostAddress) bool {
	for ,  := range  {
		if .Equal() {
			return true
		}
	}
	return false
}

// Equal tests if the HostAddress is equal to another HostAddress provided.
func ( *HostAddress) ( HostAddress) bool {
	if .AddrType != .AddrType {
		return false
	}
	return bytes.Equal(.Address, .Address)
}

// Contains tests if a HostAddress is contained within the HostAddresses struct.
func ( *HostAddresses) ( HostAddress) bool {
	for ,  := range * {
		if .Equal() {
			return true
		}
	}
	return false
}

// Equal tests if a HostAddress slice is equal to the HostAddresses struct.
func ( *HostAddresses) ( []HostAddress) bool {
	if len(*) != len() {
		return false
	}
	for ,  := range  {
		if !.Contains() {
			return false
		}
	}
	return true
}